package com.zhj.edu.exception.handler;

import com.zhj.edu.exception.notify.observer.base.Subject;
import com.zhj.edu.result.R;
import com.zhj.edu.result.ResponseCode;
import com.zhj.edu.utils.ExceptionUtils;
import com.zhj.edu.utils.ThreadPoolUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.sql.SQLException;

@ControllerAdvice
@Slf4j
public class GlobalException {

    @Autowired
    private Subject subject;

    /**
     * 此处注解的意思是：全局只要出现业务异常，就会执行到此处
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public R handlerBusinessException(BusinessException businessException){
        R error = R.error(businessException.getCode(), businessException.getMsg());
        return error;
    }

    /**
     * 处理SQL异常的方法
     * @param sqlException
     * @return
     */
    @ExceptionHandler(SQLException.class)
    @ResponseBody
    public R handlerSQLException(SQLException sqlException){
        R error = R.error(ResponseCode.SQL_EXCEPTION);
        return error;
    }

    /**
     * 处理没有权限访问的问题
     * @param accessDeniedException
     * @return
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseBody
    public R handlerAccessDeniedException(AccessDeniedException accessDeniedException){
        R error = R.error(ResponseCode.NO_PERMISSION_EXCEPTION);
        return error;
    }

    /**
     * 处理系统异常的方法(不可预期异常)
     * 不可预期异常：需要记录日志并通知运维人员
     * @param err
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R handlerSystemException(Exception err){
        R error = R.error(ResponseCode.SYSTEM_EXCEPTION);
        //错误日志级别：项目在生产环境之前（即开发和测试）时期用info级别，在生产环境用error
        //ExceptionUtils.getExceptionStackInfo(err)将异常栈信息进行打印
        log.error("出现了系统异常："+ ExceptionUtils.getExceptionStackInfo(err));
        //这里还可能有高并发的风险...故需要借助线程池来做缓存...(线程多资源占用少的情况)
        ThreadPoolUtils.getExceptionNotifyThreadPool().execute(
                new Runnable() {
                    @Override
                    public void run() {
                        //这里就是通知任务 异常出现之后才会通知（事件触发机制）
                        //事件触发机制 所对应的设计模式就是：观察者设计模式
                        subject.notifyMessage(ExceptionUtils.getExceptionStackInfo(err));
                    }
                }
        );
        return error;
    }

}
